package com.econ.powercloud.ems.util;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Map;

/**
 * @author chenxiaoqi
 * @since 2021/10/19
 */
@Slf4j
public class AsyncHttpClientUtil {

    private static final int CONNECT_TIMEOUT = 60000;
    private static final int SO_TIMEOUT = 60000;

    private static CloseableHttpAsyncClient httpClient;

    static {
        IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
                .setIoThreadCount(Runtime.getRuntime().availableProcessors())
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setSoTimeout(SO_TIMEOUT)
                .build();
        // 设置超时时间 毫秒为单位
        RequestConfig requestConfig = RequestConfig
                .copy(RequestConfig.DEFAULT)
                .setConnectTimeout(CONNECT_TIMEOUT)
                .build();
        httpClient = HttpAsyncClients
                .custom()
                .setDefaultIOReactorConfig(ioReactorConfig)
                .setDefaultRequestConfig(requestConfig)
                .build();
        httpClient.start();
    }

    /**
     * Get请求
     * @param url
     * @param callback
     */
    public static void get(String url, StringFutureCallback callback) {
        HttpUriRequest request = new HttpGet(url);
        httpClient.execute(request, callback == null ? null : new DefaultFutureCallback(callback));
    }

    /**
     * post请求
     * @param url
     * @param requestBody
     * @param contentType
     * @param callback
     */
    public static void post(String url, Object requestBody, ContentType contentType, StringFutureCallback callback) {
        HttpPost httpPost = new HttpPost(url);
        if (requestBody != null) {
            StringEntity stringEntity = null;
            String body = "";
            try {
                if(ContentType.APPLICATION_JSON == contentType){
                    body = requestBody instanceof String ? (String) requestBody : JSON.toJSONString(requestBody);
                } else if(ContentType.APPLICATION_FORM_URLENCODED == contentType){
                    body = requestBody instanceof String ? (String) requestBody : parseRequestBody(requestBody);
                }
                stringEntity = new StringEntity(body, contentType == null ? ContentType.APPLICATION_FORM_URLENCODED : contentType );
            } catch (Exception e) {
                log.error("StringEntity error", e);
            }
            httpPost.setEntity(stringEntity);
        }
        httpClient.execute(httpPost, callback == null ? null : new DefaultFutureCallback(callback));
    }

    private static String parseRequestBody(Object requestBody){
        StringBuffer requestBodyString = new StringBuffer();
        if(requestBody instanceof Map){
            Map requestBodyMap = (Map)requestBody;
            Iterator iterator = requestBodyMap.entrySet().iterator();
            while(iterator.hasNext()){
                Map.Entry entry = (Map.Entry) iterator.next();
                requestBodyString.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            return requestBodyString.substring(0, requestBodyString.length() - 1);
        } else {
            Class requestBodyClass = requestBody.getClass();
            Field[] fields = requestBodyClass.getDeclaredFields();
            for(int i = 0; i < fields.length; i++){
                try {
                    Field field = fields[i];
                    field.setAccessible(true);
                    Object value = field.get(requestBody);
                    requestBodyString.append(field.getName()).append("=").append(value).append("&");
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
            return requestBodyString.substring(0, requestBodyString.length() - 1);
        }
    }

    /**
     * 字符串类型结果回调
     */
    public interface StringFutureCallback {
        /**
         * 成功回调
         * @param content
         */
        void success(String content);
    }


    public static class DefaultFutureCallback implements FutureCallback<HttpResponse> {
        private StringFutureCallback callback;

        public DefaultFutureCallback(StringFutureCallback callback) {
            this.callback = callback;
        }

        @Override
        public void completed(HttpResponse httpResponse) {
            HttpEntity entity = httpResponse.getEntity();
            String content = "";
            try {
                content = EntityUtils.toString(entity, "UTF-8");
            } catch (IOException e) {
                log.error("EntityUtils.toString error", e);
            }
            callback.success(content);
        }

        @Override
        public void failed(Exception e) {
            log.error("http request error", e);
        }

        @Override
        public void cancelled() {
            log.debug("http request cancelled");
        }
    }
}